//******************************************************************
//******************************************************************
//**********          ANts Peer To Peer Sources        *************
//
// ANts P2P realizes a third generation P2P net. It protects your
// privacy while you are connected and makes you not trackable, hiding
// your identity (ip) and crypting everything you are sending/receiving
// from others.

// Copyright (C) 2004  Roberto Rossi

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


package ants.p2p.query;

import java.util.*;
import java.io.*;

import ants.p2p.*;
import ants.p2p.utils.encoding.StringHash;
import ants.p2p.utils.indexer.*;
import ants.p2p.filesharing.*;

import org.apache.log4j.*;

public class QueryFileListUploaderThread extends Thread{
  WarriorAnt wa;
  QueryMessage qm;
  String dest;

  static Logger _logger = Logger.getLogger(QueryFileListUploaderThread.class.getName());

  public QueryFileListUploaderThread(WarriorAnt wa, QueryMessage qm, String dest){
    this.wa = wa;
    this.qm = qm;
    this.dest = dest;
    this.setPriority(1);
  }

  public void run(){
    QueryMessage oldQm = qm;
    try {
      qm = new QueryMessage(oldQm);
      ((QueryFileListItem)qm.getQuery()).setAddAction();
      qm.getPublicHeader().generateSessionKey();
      qm.rnd.setSeed(System.currentTimeMillis());
      ArrayList resultSet = BackgroundEngine.getInstance().getCompleteFileList();
      ArrayList uploadedResultSet = new ArrayList();
      _logger.info("Uploading file list["+resultSet.size()+"] to: "+dest.substring(0,10));
      if(wa.isHttpEnabled()){
        HttpServerInfo si = new HttpServerInfo(qm.getPublicHeader().
                                               getEncryptedSessionKey(),
                                               this.wa.getHttpServerHomePage(),
                                               this.wa.getHttpServerDescription(),
                                               this.wa.getIdent(), this.wa.getLocalInetAddress(), new Long(System.currentTimeMillis()));
        si.encrypt(qm.getPublicHeader().getEncCipher());
        resultSet.add(si);
        qm.tuples.add(si);
      }
      for(int x = 0; x < resultSet.size(); x++){
        if(resultSet.get(x) instanceof String){
          QueryCompletedFileTuple qtLocal = new QueryCompletedFileTuple(
              qm.getPublicHeader().getEncryptedSessionKey(),
              (new File( (String) resultSet.get(x))).getName(),
              ( (FileInfos) BackgroundEngine.getInstance().
                        sharedFilesIndexName.get(resultSet.get(x))).getHash(),
              ( (FileInfos) BackgroundEngine.getInstance().
                        sharedFilesIndexName.get(resultSet.get(x))).getED2KHash(),
              null,
              new Long( ( (FileInfos) BackgroundEngine.getInstance().
                         sharedFilesIndexName.get(resultSet.get(x))).getSize()),
              wa.getIdent(), wa.getLocalInetAddress(),
              new Integer(wa.maxPullRequestsToServe -
                          wa.inServiceFilePullRequests.size()),
              WarriorAnt.ConnectionType,
              ( (FileInfos) BackgroundEngine.getInstance().
                        sharedFilesIndexName.get(resultSet.get(x))).getExtendedInfos(),
              ( (FileInfos) BackgroundEngine.getInstance().
                        sharedFilesIndexName.get(resultSet.get(x))).getComment());
          uploadedResultSet.add(qtLocal);
          if(BackgroundEngine.getInstance().getLastUploadedList(dest).contains(qtLocal)){
            continue;
          }
          QueryCompletedFileTuple qt = new QueryCompletedFileTuple(
              qm.getPublicHeader().getEncryptedSessionKey(),
              (new File( (String) resultSet.get(x))).getName(),
              ( (FileInfos) BackgroundEngine.getInstance().
                        sharedFilesIndexName.get(resultSet.get(x))).getHash(),
              ( (FileInfos) BackgroundEngine.getInstance().
                        sharedFilesIndexName.get(resultSet.get(x))).getED2KHash(),
              null,
              new Long( ( (FileInfos) BackgroundEngine.getInstance().
                         sharedFilesIndexName.get(resultSet.get(x))).getSize()),
              wa.getIdent(), wa.getLocalInetAddress(),
              new Integer(wa.maxPullRequestsToServe -
                          wa.inServiceFilePullRequests.size()),
              WarriorAnt.ConnectionType,
              ( (FileInfos) BackgroundEngine.getInstance().
                        sharedFilesIndexName.get(resultSet.get(x))).getExtendedInfos(),
              ( (FileInfos) BackgroundEngine.getInstance().
                        sharedFilesIndexName.get(resultSet.get(x))).getComment());
          qt.encrypt(qm.getPublicHeader().getEncCipher());
          qm.tuples.add(qt);
        }else if(resultSet.get(x) instanceof StringHash){
          BackgroundEngine be = BackgroundEngine.getInstance();
          QueryPartialFileTuple qptLocal = be.getPartialFileTuple(qm.getPublicHeader().getEncryptedSessionKey(),
              ((StringHash) resultSet.get(x)).getHash(), QueryHashItem.ANTS_HASH,
              wa.getIdent(), new Integer(wa.maxPullRequestsToServe -
                                        wa.inServiceFilePullRequests.
                                        size()), WarriorAnt.ConnectionType, false);
          uploadedResultSet.add(qptLocal);
          if(BackgroundEngine.getInstance().getLastUploadedList(dest).contains(qptLocal)){
            continue;
          }
          QueryPartialFileTuple qpt = be.getPartialFileTuple(qm.getPublicHeader().getEncryptedSessionKey(),
              ((StringHash) resultSet.get(x)).getHash(), QueryHashItem.ANTS_HASH,
              wa.getIdent(), new Integer(wa.maxPullRequestsToServe -
                                        wa.inServiceFilePullRequests.
                                        size()), WarriorAnt.ConnectionType, false);
          qpt.encrypt(qm.getPublicHeader().getEncCipher());
          qm.tuples.add(qpt);
        }

        if((x + 1) % 100 == 0 || x == resultSet.size() - 1){
          qm.setProcessed();
          qm.resetDelivered();
          qm.getPublicHeader().invalidateSessionKey();
          if (qm.getTuples().size() > 0) {
            MessageWrapper wm = wa.sendMessage(qm, dest, false, false);
            /*qm.fillMessageProperties(wa.getIdent(), dest, 0, messageID);
            wa.activateNewRouterProcess(qm, this.wa.getIdent());*/
            while (wa.myMessages.contains(wm)) {
              Thread.sleep(1000);
            }
            if (wa.failedMessages.contains(wm)) {
              wa.failedMessages.remove(wm);

              _logger.debug(wa.getShortId() +
                            ": Error in sending File list to " +
                            dest.substring(0, 10));
              BackgroundEngine.getInstance().lastTimeUploadedFileList.put(dest, new Long(0));
              BackgroundEngine.getInstance().finishedUploadingFileList();
              return;
            }
          }
          qm = new QueryMessage(oldQm);
          ((QueryFileListItem)qm.getQuery()).setAddAction();
          qm.getPublicHeader().generateSessionKey();
          qm.rnd.setSeed(System.currentTimeMillis());
        }
      }

      _logger.info("Last uploaded file list["+BackgroundEngine.getInstance().getLastUploadedList(dest).size()+"]");
      ArrayList removeList = (ArrayList)BackgroundEngine.getInstance().getLastUploadedList(dest).clone();
      removeList.removeAll(uploadedResultSet);
      _logger.info("To be removed file list["+removeList.size()+"]");
      ArrayList curUploadedList = ((ArrayList)uploadedResultSet.clone());
      BackgroundEngine.getInstance().setLastUploadedList(dest, curUploadedList);
      curUploadedList.addAll(removeList);

      qm = new QueryMessage(oldQm);
      ( (QueryFileListItem) qm.getQuery()).setRemoveAction();
      qm.getPublicHeader().generateSessionKey();
      qm.rnd.setSeed(System.currentTimeMillis());
      _logger.info("Uploading delete file list to: "+dest.substring(0,10));
      for(int x = 0; x < removeList.size(); x++){
        curUploadedList.remove(removeList.get(x));
        QueryFileTuple qft = (QueryFileTuple)removeList.get(x);
        qft.sessionKey = qm.getPublicHeader().getEncryptedSessionKey();
        qft.encrypt(qm.getPublicHeader().getEncCipher());
        qm.tuples.add(qft);

        if((x + 1) % 100 == 0 || x == removeList.size() - 1){
          qm.setProcessed();
          qm.resetDelivered();
          qm.getPublicHeader().invalidateSessionKey();
          if (qm.getTuples().size() > 0) {
            MessageWrapper wm = wa.sendMessage(qm, dest, false, false);
            /*qm.fillMessageProperties(wa.getIdent(), dest, 0, messageID);
            wa.activateNewRouterProcess(qm, this.wa.getIdent());*/
            while (wa.myMessages.contains(wm)) {
              Thread.sleep(1000);
            }
            if (wa.failedMessages.contains(wm)) {
              wa.failedMessages.remove(wm);

              _logger.debug(wa.getShortId() +
                            ": Error in sending File list to " +
                            dest.substring(0, 10));
              BackgroundEngine.getInstance().lastTimeUploadedFileList.put(dest, new Long(0));
              BackgroundEngine.getInstance().finishedUploadingFileList();
              return;
            }
          }
          qm = new QueryMessage(oldQm);
          ((QueryFileListItem)qm.getQuery()).setRemoveAction();
          qm.getPublicHeader().generateSessionKey();
          qm.rnd.setSeed(System.currentTimeMillis());
        }
      }

      BackgroundEngine.getInstance().setLastUploadedList(dest, curUploadedList);
      BackgroundEngine.getInstance().finishedUploadingFileList();
      _logger.info("Uploaded file list["+BackgroundEngine.getInstance().getLastUploadedList(dest).size()+"]");
    }
    catch (Exception e) {
      _logger.error("",e);
      BackgroundEngine.getInstance().finishedUploadingFileList();
    }
  }
}
